﻿using UnityEngine;
using System.Collections;
using System.Collections.Generic;

public enum FurnitureType
{
    None,
    Floor,
    Wall,
    Toilet, Bathtub, Laundromat, LaundryBasket, Sink,
    Desk,
    Chair,
    Couch,
    Table,
    Carpet,
    Bed,
    Closet,
    Printer
}

public class FurniturePiece : MonoBehaviour{

    public FurnitureType furnitureType = 0; //to which furniture type this object belongs
    //public Face[] faces; //for each face, the constraints have to be set (face and type of the parent object)

    private List<RoomType> possibleRooms = null; //for which rooms this piece of shit is allowed

    public List<MultiDimensionalFace> face0; //TODO: public get, private set 
    public List<MultiDimensionalFace> face1;
    public List<MultiDimensionalFace> face2;
    public List<MultiDimensionalFace> face3;
    public List<MultiDimensionalFace> face4;
    public List<MultiDimensionalFace> face5;

    private bool groupConstraint = false;

    public bool addedToGroup = false;

                //1         //box seen from above with faces
            /**********/
            /*        */
    //2     /*        */  //3
            /*        */
            /**********/
                //0

    private float distanceFromParent = 0.1f;

    private bool ignoreCollision = false;

    private Vector3[] facesPos = new Vector3[6];

    private Vector3[] facesNormal = new Vector3[6];

    private int minInstances = 1;

    private int maxInstancesAsChild = 1; //TODO: vielleicht eher in die Richtung 'ratio', also ein Verhältnis zur Größe des Raumes (pro Width z.b. 0.1 oder so)

    private int maxInstancesInRoom = 1;

    private int definitiveMaxNumber = 0;

    private int existingInstances = 0;

    private Vector3 otherColliderSize = new Vector3(0,0,0);


//    private Face[] connectionFaces = null; //0 front, 1 back, 2 left, 3 right, 4 top, 5 bottom
    private FurniturePiece parent = null; //the parent of the object

    int i = 0;
    int maximumIValue = 3;

    private float borderThickness = 2;

    private bool colliding = false;


    private float displacementFactor = 0.1f;

    private int numerOfLoops = 0;

    private int wallThickness = DataStorage.Instance.GetWallThickness();

    //public FurnitureType[] possibleParents = null; //**this is specified in the face attribute**which objects are a possible parent for this piece

    void Start()
    {
        SetFaceNormalsAndPositions();
    }

    void Update()
    {

    }

    private FurniturePiece()
    {
        possibleRooms = new List<RoomType>();
        face0 = new List<MultiDimensionalFace>();
        face1 = new List<MultiDimensionalFace>();
        face2 = new List<MultiDimensionalFace>();
        face3 = new List<MultiDimensionalFace>();
        face4 = new List<MultiDimensionalFace>();
        face5 = new List<MultiDimensionalFace>();
    }

    public void Clean()
    {
        possibleRooms = new List<RoomType>();

        face0.Clear();
        face1.Clear();        
        face2.Clear();
        face3.Clear();
        face4.Clear();
        face5.Clear();
        
        face0 = new List<MultiDimensionalFace>();
        face1 = new List<MultiDimensionalFace>();
        face2 = new List<MultiDimensionalFace>();
        face3 = new List<MultiDimensionalFace>();
        face4 = new List<MultiDimensionalFace>();
        face5 = new List<MultiDimensionalFace>();
    }

    public void SetPossibleRooms(RoomType type)
    {
        this.possibleRooms.Add(type);
    }

    public void SetFace(int faceNo, int _connectingFace, FurnitureType type)
    {
        if (faceNo == 0)
        {
            this.face0.Add(new MultiDimensionalFace(_connectingFace, type));
        }
        else if (faceNo == 1)
        {
            face1.Add(new MultiDimensionalFace(_connectingFace, type));
        }
        else if (faceNo == 2)
        {
            face2.Add(new MultiDimensionalFace(_connectingFace, type));
        }
        else if (faceNo == 3)
        {
            face3.Add(new MultiDimensionalFace(_connectingFace, type));
        }
        else if (faceNo == 4)
        {
            face4.Add(new MultiDimensionalFace(_connectingFace, type));
        }
        else if (faceNo == 5)
        {
            face5.Add(new MultiDimensionalFace(_connectingFace, type));
        }
    }

    public void ClearFace(int faceNo)
    {
        if (faceNo == 0)
        {
            face0.Clear();
        }
        else if (faceNo == 1)
        {
            face1.Clear();
        }
        else if (faceNo == 2)
        {
            face2.Clear();
        }
        else if (faceNo == 3)
        {
            face3.Clear();
        }
        else if (faceNo == 4)
        {
            face4.Clear();
        }
        else if (faceNo == 5)
        {
            face5.Clear();
        }
    }

    public bool MoveTo(MultiDimensionalFace face, int faceNo, FurniturePiece destinationObject, GameObject floor, List<GameObject> placeholderList, List<GameObject> furnitureListRoot, List<GameObject> furnitureListChildren, bool secondChild) 
    {
        SetFaceNormalsAndPositions();

        int loopCounter = 0;
        while (true)
        {
            float a = Vector3.Dot(facesNormal[faceNo], facesNormal[faceNo]); //dot products
            float b = Vector3.Dot(facesNormal[faceNo], destinationObject.facesNormal[face.connectingFace]);
            float e = Vector3.Dot(destinationObject.facesNormal[face.connectingFace], destinationObject.facesNormal[face.connectingFace]);

            float d = a * e - b * b;

            if (d != 0.0 || facesNormal[faceNo].Equals(destinationObject.facesNormal[face.connectingFace])) //if d=0.0, the vectors are parallel, if they are equal, the point in the same direction (which they shouldn't)
                RotateFurniture();
            else
                break;

            if (loopCounter > 4) //because there are a maxium of 4 rotations until every possibility is used
                break;
            loopCounter++;
        }

        string connectingFace = face.connectingFace.ToString();

        Vector3 facePosOfPlannedParent = (Vector3)destinationObject.facesPos.GetValue(face.connectingFace);

        Vector3 displacementVector = facePosOfPlannedParent - facesPos[faceNo];/*From"faceParam"*/

        displacementVector.z = (float)floor.GetComponent<FurniturePiece>().facesPos[4].z - facesPos[5].z;

        if (facesNormal[faceNo].y != 0) //if the furniture has not been rotated or rotated for 180°, the y-part of the vector will be != 0
        {
            float temp1 = destinationObject.collider.bounds.min.x;
            float temp2 = collider.bounds.extents.x;
            float leftMostPositionX = 0;
            if (secondChild)
                leftMostPositionX = destinationObject.collider.bounds.min.x + collider.bounds.extents.x; //leftmost side of the wall + the thickness of the wall + half of the width of the furniture
            else
                leftMostPositionX = destinationObject.collider.bounds.min.x + wallThickness + collider.bounds.extents.x; //leftmost side of the wall + the thickness of the wall + half of the width of the furniture

            transform.position = new Vector3(leftMostPositionX, transform.position.y - collider.bounds.center.y + displacementVector.y, transform.position.z + displacementVector.z); //TODO: Objekt in Ecke setzen, dann ist das verschieben einfacher
        }
        else if (facesNormal[faceNo].x != 0) //if the furniture has been rotated for 90° or 270°, the x-part of the vector will be != 0
        {
            float lowerMostPositionY = 0;
            if (secondChild)
                lowerMostPositionY = destinationObject.collider.bounds.min.y + collider.bounds.extents.y; //lowermost side of the wall + half of the height of the furniture
            else
                lowerMostPositionY = destinationObject.collider.bounds.min.y + wallThickness + collider.bounds.extents.y; //lowermost side of the wall + half of the height of the furniture

            transform.position = new Vector3(transform.position.x - collider.bounds.center.x + displacementVector.x, lowerMostPositionY, transform.position.z + displacementVector.z); //TODO: displacementVector muss je nach der Wand positiv oder negativ sein
        }

        transform.position = transform.position - (facesNormal[faceNo] * distanceFromParent); //Move the object a bit into the opposite direction where the face connects to the parent, so they don't intersect
        if (facesNormal[faceNo].y != 0) //also move it a bit away from the other wall
        {
            transform.position = new Vector3(transform.position.x + distanceFromParent, transform.position.y, transform.position.z);
        }
        else if (facesNormal[faceNo].x != 0)
        {
            transform.position = new Vector3(transform.position.x, transform.position.y + distanceFromParent, transform.position.z);
        }

        if (ignoreCollision || MoveUntilNotColliding(faceNo, placeholderList, furnitureListRoot, furnitureListChildren, destinationObject))
        {
            SetFaceNormalsAndPositions();
            return true;
        }
        else
           return false;
    }

    private bool MoveUntilNotColliding(int _faceNo, List<GameObject> _placeholderList, List<GameObject> _furnitureListRoot, List<GameObject> _furnitureListChildren, FurniturePiece destinationObject) //TODO: include doorPlaceholders
    {
        numerOfLoops = 0;

        List<GameObject> allFurniture = new List<GameObject>(); //Combining the 3 lists
        allFurniture.AddRange(_placeholderList);
        allFurniture.AddRange(_furnitureListRoot);
        allFurniture.AddRange(_furnitureListChildren);
        foreach (GameObject go in _furnitureListChildren)
        {
            Transform temp = go.transform.Find("ColliderExtension");
            if (temp != null)
                allFurniture.Add(temp.gameObject);
        }
        while (true && numerOfLoops < 3)
        {
            foreach (GameObject placedFurnitureElement in allFurniture)
            {
                int i = 0; //TODO DRINGEND RAUS DAMIT!!
                if (!placedFurnitureElement.Equals(this.gameObject))
                {
                    while (this.collider.bounds.Intersects(placedFurnitureElement.collider.bounds) && i < 100)
                    {
                        if (placedFurnitureElement.name.Contains("Wall") || placedFurnitureElement.name.Contains("room"))
                            //Destroy(gameObject);
                            return false;
                        if (facesNormal[_faceNo].y != 0) //if the furniture has not been rotated or rotated for 180°, the y-part of the vector will be != 0
                        {
                            if (collider.bounds.max.x > destinationObject.collider.bounds.max.x) //if the furniture is outside of the planned parent's boundaries
                                return false;
                            transform.position = new Vector3(transform.position.x + displacementFactor, transform.position.y, transform.position.z);
                        }
                        else if (facesNormal[_faceNo].x != 0) //if the furniture has been rotated for 90° or 270°, the x-part of the vector will be != 0
                        {
                            if (collider.bounds.max.y > destinationObject.collider.bounds.max.y) //if the furniture is outside of the planned parent's boundaries
                                return false;
                            transform.position = new Vector3(transform.position.x, transform.position.y + displacementFactor, transform.position.z);
                        }
                        i++;
                    }
                }
            }
            numerOfLoops++;
        }

        //if (colliding)
        //{

        //}

        return true;
    }

    public bool MoveOnFloor(MultiDimensionalFace face, int faceNo, GameObject floor, List<GameObject> _placeholderList, List<GameObject> _furnitureListRoot, List<GameObject> _furnitureListChildren)
    {
        List<GameObject> allFurniture = new List<GameObject>(); //Combining the 3 lists

        allFurniture.AddRange(_placeholderList);
        allFurniture.AddRange(_furnitureListRoot);
        allFurniture.AddRange(_furnitureListChildren);

        SetFaceNormalsAndPositions();

        float floorPossibleXLeft = floor.collider.bounds.min.x + borderThickness + Mathf.Abs(transform.position.x - collider.bounds.min.x);
        float floorPossibleXRight = floor.collider.bounds.max.x - borderThickness - Mathf.Abs(transform.position.x - collider.bounds.max.x);
        float floorPossibleYBottom = floor.collider.bounds.min.y + borderThickness + Mathf.Abs(transform.position.y - collider.bounds.min.y);
        float floorPossibleYTop = floor.collider.bounds.max.y - borderThickness - Mathf.Abs(transform.position.y - collider.bounds.max.y);

        //int loopCounter = 0;
        //while (true)
        //{
        //    float a = Vector3.Dot(facesNormal[faceNo], facesNormal[faceNo]); //dot products
        //    float b = Vector3.Dot(facesNormal[faceNo], destinationObject.facesNormal[face.connectingFace]);
        //    float e = Vector3.Dot(destinationObject.facesNormal[face.connectingFace], destinationObject.facesNormal[face.connectingFace]);
        //    Debug.Log("XXX" + facesNormal[faceNo]);
        //    Debug.Log(destinationObject.facesNormal[face.connectingFace]);

        //    float d = a * e - b * b;

        //    if (d != 0.0 || facesNormal[faceNo].Equals(destinationObject.facesNormal[face.connectingFace])) //if d=0.0, the vectors are parallel, if they are equal, the point in the same direction (which they shouldn't)
        //        RotateFurniture();
        //    else
        //        break;

        //    if (loopCounter > 4) //because there are a maxium of 4 rotations until every possibility is used
        //        break;
        //    loopCounter++;
        //}
        if (gameObject.name.Contains("Laundry"))
            Debug.Log("");

        int numberOfRotations = Random.Range(0, 4);
        for(int i=0; i<numberOfRotations; i++)
            RotateFurniture();
        
        string connectingFace = face.connectingFace.ToString();

        Vector3 facePosOfPlannedParent = (Vector3)floor.GetComponent<FurniturePiece>().facesPos[4];

        Vector3 displacementVector = facePosOfPlannedParent - facesPos[5];/*From"faceParam"*/

        int loopCounter = 0;

        transform.position = new Vector3(transform.position.x, transform.position.y, transform.position.z + displacementVector.z);

        while (true && loopCounter <= 20)
        {
            float positionX = Random.Range(floorPossibleXLeft, floorPossibleXRight);
            float positionY = Random.Range(floorPossibleYBottom, floorPossibleYTop);

            transform.position = new Vector3(positionX, positionY, transform.position.z); //TODO: displacementVector muss je nach der Wand positiv oder negativ sein
            SetFaceNormalsAndPositions();
            loopCounter++;
            foreach (GameObject placedFurnitureElement in allFurniture)
            {
                if (!placedFurnitureElement.Equals(this.gameObject))
                {
                    if (this.collider.bounds.Intersects(placedFurnitureElement.collider.bounds))
                    {
                        Debug.Log("floor colliding with " + placedFurnitureElement.name);
                        loopCounter++;
                        break;
                    }
                }
            }
            Debug.Log("LoopCounterFloorMoving " + loopCounter);
        }

            return true;
    }

    public void RotateFurniture()
    {
        if (gameObject.name.Contains("OfficeTable"))
        {
            for (int k = 0; k <= 3; k++)
            {
                Debug.Log("beginning: face " + k + " pos: " + facesPos[k]);
                Debug.Log("normal " + k + " pos: " + facesNormal[k]);

            }
        }


        i++;
        if (i > maximumIValue)
            i=0;
        int j = i;

        Vector3[] faces1To4 = new Vector3[4];

        //this.transform.Rotate(Vector3.forward * 90); //counter-clockwise rotation
        this.transform.Rotate(Vector3.forward * 90, Space.World); //counter-clockwise rotation


        faces1To4[0] = transform.position - Vector3.up * collider.bounds.extents.y; //originally face0
        faces1To4[1] = transform.position + Vector3.right * collider.bounds.extents.x; //originally face3
        faces1To4[2] = transform.position + Vector3.up * collider.bounds.extents.y; //originally face1
        faces1To4[3] = transform.position - Vector3.right * collider.bounds.extents.x; //originally face2

        facesPos[0] = faces1To4[j]; //this method reflects a rotation counter-clockwise (positive rotation degree)
        j++;
        if (j > maximumIValue)
            j = 0;
        facesPos[3] = faces1To4[j];
        j++;
        if (j > maximumIValue)
            j = 0;
        facesPos[1] = faces1To4[j];
        j++;
        if (j > maximumIValue)
            j = 0;
        facesPos[2] = faces1To4[j];

        facesNormal[0] = facesPos[0] - transform.position;
        facesNormal[0].Normalize();
        facesNormal[1] = facesPos[1] - transform.position;
        facesNormal[1].Normalize();
        facesNormal[2] = facesPos[2] - transform.position;
        facesNormal[2].Normalize();
        facesNormal[3] = facesPos[3] - transform.position;
        facesNormal[3].Normalize();

        if (gameObject.name.Contains("OfficeTable"))
        {
            for (int k = 0; k <= 3; k++)
            {
                Debug.Log("end face " + k + " pos: " + facesPos[k]);
            }
        }
        //face5Normal = face5Pos - transform.position;
        //face6Normal = face6Pos - transform.position;
    }

    public void SetFaceNormalsAndPositions() //WICHTIG!!! wenn das nach RotateFurniture() aufgerufen wird, werden die Anpassungen der Faces wieder zurückgesetzt!!!!!
    {
        int j = i;

        Vector3[] faces1To4 = new Vector3[4];

        Vector3 ads = collider.bounds.extents;
        Vector3 afs = collider.bounds.extents;


        faces1To4[0] = transform.position - Vector3.up * collider.bounds.extents.y; //originally face0
        faces1To4[1] = transform.position + Vector3.right * collider.bounds.extents.x; //originally face3
        faces1To4[2] = transform.position + Vector3.up * collider.bounds.extents.y; //originally face1
        faces1To4[3] = transform.position - Vector3.right * collider.bounds.extents.x; //originally face2

        facesPos[0] = faces1To4[j]; //this method reflects a rotation counter-clockwise (positive rotation degree)
        j++;
        if (j > maximumIValue)
            j = 0;
        facesPos[3] = faces1To4[j];
        j++;
        if (j > maximumIValue)
            j = 0;
        facesPos[1] = faces1To4[j];
        j++;
        if (j > maximumIValue)
            j = 0;
        facesPos[2] = faces1To4[j];

        facesNormal[0] = facesPos[0] - transform.position;
        facesNormal[0].Normalize();
        facesNormal[1] = facesPos[1] - transform.position;
        facesNormal[1].Normalize();
        facesNormal[2] = facesPos[2] - transform.position;
        facesNormal[2].Normalize();
        facesNormal[3] = facesPos[3] - transform.position;
        facesNormal[3].Normalize();

        facesPos[4] = transform.position - Vector3.forward * collider.bounds.extents.z; //stays the same when rotated
        facesPos[5] = transform.position + Vector3.forward * collider.bounds.extents.z; //stays the same when rotated
        facesNormal[4] = facesPos[4] - transform.position;
        facesNormal[4].Normalize();
        facesNormal[5] = facesPos[5] - transform.position;
        facesNormal[5].Normalize();
        //facesPos[0] = transform.position - Vector3.up * collider.bounds.extents.y; 
        //facesPos[1] = transform.position + Vector3.up * collider.bounds.extents.y;
        //facesPos[2] = transform.position - Vector3.right * collider.bounds.extents.x;
        //facesPos[3] = transform.position + Vector3.right * collider.bounds.extents.x;
        

        //facesNormal[0] = facesPos[0] - transform.position;
        //facesNormal[0].Normalize();
        //facesNormal[1] = facesPos[1] - transform.position;
        //facesNormal[1].Normalize();
        //facesNormal[2] = facesPos[2] - transform.position;
        //facesNormal[2].Normalize();
        //facesNormal[3] = facesPos[3] - transform.position;
        //facesNormal[3].Normalize();

    }

    public int GetMaxInstancesAsChild()
    {
        return maxInstancesAsChild;
    }
    public void SetMaxInstancesAsChild(int value)
    {
        maxInstancesAsChild = value;
    }

    public int GetMaxInstancesInRoom()
    {
        return maxInstancesInRoom;
    }
    public void SetMaxInstancesInRoom(int value)
    {
        maxInstancesInRoom = value;
    }

    public int GetMinInstances()
    {
        return minInstances;
    }
    public void SetMinInstances(int value)
    {
        minInstances = value;
    }

    public FurnitureType GetFurnitureType()
    {
        return furnitureType;
    }
    public void SetFurnitureType(FurnitureType type)
    {
        furnitureType = type;
    }

    public int GetExistingInstances()
    {
        return existingInstances;
    }
    public void AddInstance()
    {
        existingInstances++;
    }

    public bool GetIgnoreCollisions()
    {
        return ignoreCollision;
    }
    public void SetIgnoreCollisionsTrue()
    {
        ignoreCollision = true;
    }

    public ArrayList GetChangeableProperties()
    {
        ArrayList array = new ArrayList();
        array.Add(ignoreCollision);
        array.Add(maxInstancesAsChild);
        array.Add(maxInstancesInRoom);
        array.Add(otherColliderSize);
        return array;
    }
    public void SetChangeableProperties(ArrayList _array)
    {
        ignoreCollision = (bool)_array[0];
        maxInstancesAsChild = (int)_array[1];
        maxInstancesInRoom = (int)_array[2];
        otherColliderSize = (Vector3)_array[3];
    }

    public int GetDefinitiveMaxNumber()
    {
        return definitiveMaxNumber;
    }
    public void SetDefinitiveMaxNumber(int value)
    {
        definitiveMaxNumber = value;
    }

    public bool GetGroupConstraint()
    {
        return groupConstraint;
    }
    public void  SetGroupConstraint(bool value)
    {
        groupConstraint = value;
    }

    public void SetOtherColliderValues(Vector3 colliderValues)
    {
        otherColliderSize = colliderValues;
    }

    public void SwitchColliders()
    {
        //BoxCollider[] boxColliders = gameObject.GetComponents<BoxCollider>();
        //BoxCollider[] boxCollidersCopy = (BoxCollider[])boxColliders.Clone();

        //Destroy(gameObject.GetComponent<BoxCollider>());
        if (!otherColliderSize.Equals(Vector3.zero))
        {
            Vector3 colliderSizeTemp = gameObject.GetComponent<BoxCollider>().size;
            gameObject.GetComponent<BoxCollider>().size = otherColliderSize;
            otherColliderSize = colliderSizeTemp;
            SetFaceNormalsAndPositions();
        }
        //if (boxColliders != null && boxColliders.Length > 1)
        //{
        //    //for (int i = 0; i < boxColliders.Length; i++)
        //    //{
        //    //    if (boxColliders[i].enabled == false)
        //    //        boxColliders[i].enabled = true;
        //    //    else if (boxColliders[i].enabled == true)
        //    //        boxColliders[i].enabled = false;
        //    //}
        //    boxColliders[0] = boxCollidersCopy[1];
        //    boxColliders[1] = boxCollidersCopy[0];

        //}
    }
}


